package cn.edu.jxau.core.controller;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import cn.edu.jxau.core.cache.Cache;
import cn.edu.jxau.core.cache.LFUCache;
import cn.edu.jxau.core.controller.hander.AjaxResultHander;
import cn.edu.jxau.core.util.ResponseUtils;
import cn.edu.jxau.core.util.upload.FileUpload;
import cn.edu.jxau.framework.dao.core.ParseXML;

/**
 * 作为核心控制器,根据service.xml的name值匹配URL请求调用对应的Service TestServlet
 *
 * @author zclong 2017年8月5日
 */
public class CoreServlet extends ConfigServlet {
    private static Logger logger = LoggerFactory.getLogger(CoreServlet.class);
    public static String ENCODING = "UTF-8";
    private static final long serialVersionUID = 1L;
    private FileUpload fileUpload = new FileUpload();
    // K:path V:path去除.do/.action的service配置
    public static Cache<String, ServiceConfig> serviceConfigCache;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        long start = System.currentTimeMillis();
        // 初始化即开始加载数据库的配置文件
        ParseXML.CreateParseXMLInstance("/spectrumSql.xml");
        // 初始化缓存
        serviceConfigCache = new LFUCache<String, ServiceConfig>(100);
        long end = System.currentTimeMillis();
        logger.debug("核心转发器初始化完成，花费了: {} 毫秒", (end - start));
    }

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        setCharacterEncoding(request, response); // 编码处理
        // 将请求映射到目标service上
        ServiceConfig targetService = request2ServiceConfig(request);
        if (null == targetService) {
            notfound(request, response);
            return;
        }
        try {
            // 请求 的所有数据(普通数据,验证码,上传信息)
            Map<String, Object[]> params = getRequestParams(request, targetService); // 处理前台传来的参数
            // 获取登录成功者的session信息
            HashMap<String, Object> user_session = (HashMap<String, Object>) request.getSession()
                    .getAttribute("session_user");
            if (null != user_session) { // 若session存在说明已经登录，将用户的ID封装进params中
                String[] userID = {user_session.get("userID") + ""};
                params.put("userID", userID);
            }
            Map<String, Object> data = new HashMap<String, Object>(); // 存放service层处理后的数据

            logger.info("访问了该页面：" + targetService.getName() + " data: " + JSON.toJSONString(params));
            // 创建Service对象
            new Service(targetService, params, data);
            // 得到要跳转的页面
            String result = (String) data.get("result");
            // 处理结果
            handleResult(request, response, data, targetService.getResultConfig(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将请求映射到ServiceConfig
     *
     * @param request
     * @return
     * @zclong
     * @2017年8月5日
     * @ReturnType: ServiceConfig
     */
    public static ServiceConfig request2ServiceConfig(HttpServletRequest request) {

        String path = request.getRequestURI(); // path="/login.do"     
        path = path.substring(path.lastIndexOf("/"));
        ServiceConfig cache = serviceConfigCache.get(path);
        if (null != cache) {
            logger.debug("cache : " + cache);
            return cache;
        }

        // 获取请求名
        String reqName = path.substring(0, path.lastIndexOf(".")); // reqName=login
        // ServiceMapper：根据请求路径去加载配置文件，得到相应的方法，信息
        cache = ServiceMapper.getServiceConfig(reqName);
        serviceConfigCache.put(path, cache); // 加入到缓存
        // ServiceConfig service= ServiceMapper.getServiceConfig(reqName);
        return cache;
    }

    /**
     * 获取请求的数据
     *
     * @param request
     * @param targetService
     * @return
     * @zclong
     * @2017年8月5日
     * @ReturnType: Map<String,String[]>
     */
    private Map<String, Object[]> getRequestParams(HttpServletRequest request, ServiceConfig targetService) {
        Map<String, Object[]> results = new HashMap<>();
        Map<String, String[]> params = request.getParameterMap(); // 前台传入的普通数据
        if (null != params && 0 != params.size()) {
            results.putAll(params);
        }
        if (request.getRequestURI().matches("^/upload/.++$")) {
            File[] files = fileUpload.doSava(request, FileUpload.SWAPPATH, results);
            if (files != null && files.length > 0) {
                results.put("file", files);
            }
        }
        return results;
    }

    /**
     * 处理Service执行后的结果
     *
     * @throws IOException
     * @throws ServletException
     */
    private void handleResult(HttpServletRequest request, HttpServletResponse response, Map<String, Object> data,
                              ServiceResultConfig result) throws IOException, ServletException {
        if (null == result) {
            return; // 若完成业务什么也不做，即不用跳转到其他页面，返回业务方法返回null;
        }
        
        String type = result.getType(); // type= dispatcher表示是转发还是重定向或者返回json信息

        String resultName = result.getName(); // resultName = login 即配置文件里的信息

        // <result name="login" type="dispatcher">/index.jsp</result>
        String path = result.getLocation(); // path : /index.jsp,与service.xml相对应

        // 登录处理
        if (ServiceResultConfig.LOGIN.equals(resultName)) {
            //重复登录强制将原来用户注销退出
//            int status = (int) data.get("status");
//            int userID = (int) data.get("userID");
            HashMap<String, Object> user = (HashMap<String, Object>) data.get("session_user");
            HttpSession session = request.getSession();
           //session.getServletContext()得到时application对象
            ServletContext application=session.getServletContext();
            /*if (1 == status) { // 重复登录
                // 获取登录成功者的session信息
                HashMap<String, Object> user_session = (HashMap<String, Object>) request.getSession()
                        .getAttribute("session_user");
                if (null != user_session) { // 若session存在说明已经登录，将用户的ID封装进params中
                    int id = (int) user_session.get("userID");
                    if (id == userID) {
                        request.getSession().invalidate(); //销毁session
                    }
                }
            }*/
            Map<Integer, String> loginMap = (Map<Integer, String>) application.getAttribute("loginMap");
            if(loginMap == null) {
                loginMap = new HashMap<Integer, String>();
            }
            for(Integer key : loginMap.keySet()) {
                if(user.get("userID") == key) {
                    if(session.getId().equals(loginMap.get(key))) {
                        logger.debug(user.get("email") + "用户已经登录！");
                        request.getRequestDispatcher("/login.jsp").forward(request, response);
                        return;
                    }else {
                        logger.debug(user.get("email") + "异地登录被拒绝！");
                        request.getRequestDispatcher("/login.jsp").forward(request, response);
                        return;
                    }
                }
            }
            loginMap.put((Integer) user.get("userID"), session.getId());
            application.setAttribute("loginMap", loginMap);
            for (Entry<String, Object> entry : data.entrySet()) {
                session.setAttribute(entry.getKey(), entry.getValue()); // 设置用户session
            }
        } else if (ServiceResultConfig.EXIT.equals(resultName)) { // 注销
            request.getSession().invalidate(); //销毁session
        }

        // 处理登陆失败验证码问题
        if (ServiceResultConfig.FAILED.equals(resultName)) {
            for (Entry<String, Object> entry : data.entrySet()) {
                request.getSession().setAttribute(entry.getKey(), entry.getValue()); // 设置错误信息session，包括登录失败次数
            }
        }

        // 保存其他业务的所有数据，即将数据传递给要转发的页面
        if (!data.isEmpty()) {
            for (Entry<String, Object> entry : data.entrySet()) {
                request.setAttribute(entry.getKey(), entry.getValue());
            }
        }

//        //上传光谱
//        if(data.get("uploadMessage")!=null){
//            request.getSession().setAttribute("uploadMessage", data.get("uploadMessage"));
//            System.out.println("||||||||||||||||"+data.get("uploadMessage"));
//        }
        // 转发
        if (ServiceResultConfig.TYPE_DISPATCHER.equals(type)) {
            request.getRequestDispatcher(path).forward(request, response);
            return;
        }
        // 重定向
        if (ServiceResultConfig.TYPE_REDIRECT.equals(type)) {
            response.sendRedirect(request.getContextPath() + result.getLocation());
            return;
        }

        // 异步请求/JSON数据
        if (ServiceResultConfig.TYPE_AJAX.equals(type)) {
            new AjaxResultHander(request, response).doAjax(data);
            return;
        }

        // 文件下载
        if (ServiceResultConfig.TYPE_DOWNLOAD.equals(type)) {
            ResponseUtils.download((File) data.get("file"), request, response);
        }
    }

    /**
     * 处理编码问题
     *
     * @param request
     * @param response
     * @zclong
     * @2017年8月5日
     * @ReturnType: void
     */
    public static void setCharacterEncoding(HttpServletRequest request, HttpServletResponse response) {
        try {
            if (null != request) {
                request.setCharacterEncoding(ENCODING);
            }
            if (null != response) {
                response.setCharacterEncoding(ENCODING);
                response.setContentType("text/html;charset=" + ENCODING); // 处理响应编码
            }
        } catch (UnsupportedEncodingException e) {
        }
    }

    /**
     * 若请求不存在则转发到404页面
     *
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     * @zclong
     * @2017年8月5日
     * @ReturnType: void
     */
    public static void notfound(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        request.getRequestDispatcher("/WEB-INF/jsps/404.jsp").forward(request, response);
    }
}
